import { useEffect, useMemo } from 'react'
import { Field, Form, Formik, useFormikContext } from 'formik'
import { UserAccessGroup, UserAccessPermission } from '@prisma/client'
import { trpc, inferQueryOutput, inferMutationInput } from '~/utils/trpc'
import IVInputField from '~/components/IVInputField'
import IVButton from '~/components/IVButton'
import PageHeading from '~/components/PageHeading'
import { notify } from '~/components/NotificationCenter'
import { useHasPermission } from '~/components/DashboardContext'
import { DialogStateReturn } from 'reakit'
import IVDialog, { useDialogState } from '~/components/IVDialog'
import PermissionSelector from '~/components/PermissionSelector'
import SimpleTable from '~/components/SimpleTable'
import { dateTimeFormatter } from '~/utils/formatters'
import IVAPIError from '~/components/IVAPIError'
import UsersList from '~/components/UsersList'
import useTable from '~/components/IVTable/useTable'
import { useOrgParams } from '~/utils/organization'
import IVAlert from '~/components/IVAlert'
import IconInfo from '~/icons/compiled/Info'
import TeamsSelectorProps from '~/components/TeamsSelector'
export default function UsersPage() {
useHasPermission('READ_USERS', { redirectToDashboardHome: true })
const canAddUsers = useHasPermission('WRITE_USERS')
const { envSlug } = useOrgParams()
const addUserDialog = useDialogState()
const users = trpc.useQuery(['dashboard.users.index'])
const teams = trpc.useQuery(['group.list'])
return (
addUserDialog.show(),
disabled: !canAddUsers,
},
]}
/>
{envSlug && (
Users persist across all environments.
)}
{users.data && (
<>
>
)}
{canAddUsers && (
)}
)
}
interface AddUserFormState
extends Omit, 'permissions'> {
role: UserAccessPermission
}
function AddUserDialog({
onSubmit,
dialog,
teams,
}: {
onSubmit?: () => void
dialog: DialogStateReturn
teams: Pick[]
}) {
const addUser = trpc.useMutation('organization.add-user')
const isHidden = !dialog.visible && !dialog.animating
const { reset: resetMutation } = addUser
useEffect(() => {
if (isHidden) resetMutation()
}, [isHidden, resetMutation])
return (
initialValues={{
email: '',
role: 'ACTION_RUNNER',
groupIds: [],
}}
onSubmit={async ({ email, role, groupIds }) => {
if (addUser.isLoading) return
addUser.mutate(
{
email,
permissions: [role],
groupIds,
},
{
async onSuccess() {
dialog.hide()
if (onSubmit) onSubmit()
notify.success(`An invitation was sent to ${email}.`)
},
}
)
}}
>
)
}
function ResetFormToken({ isResetReady }: { isResetReady: boolean }) {
const { resetForm } = useFormikContext()
useEffect(() => {
if (isResetReady) resetForm()
}, [resetForm, isResetReady])
return null
}
function PendingInvitationsTable({
pendingInvitations,
}: {
pendingInvitations: inferQueryOutput<'dashboard.users.index'>['pendingInvitations']
}) {
const { mutate } = trpc.useMutation('organization.revoke-invitation')
const { refetchQueries } = trpc.useContext()
const rows = useMemo(() => {
return pendingInvitations.map((invitation, idx) => {
return {
key: invitation.id,
data: {
email: invitation.email,
sentAt: dateTimeFormatter.format(invitation.createdAt),
action: (
),
},
}
})
}, [pendingInvitations, mutate, refetchQueries])
const table = useTable({
data: rows,
columns: ['Email', 'Sent at', ''],
// sorting tables w/ react components still needs some work; disable sorting until that works
isSortable: false,
shouldCacheRecords: false,
})
if (!rows.length) return null
return (
Pending invitations
)
}